use std::path::PathBuf;

use crate::{bootstrap::AppState, middleware::jwt::admin::Claims, router::resp::RespVO};
use axum::{
    body::Body,
    extract::{multipart, Multipart, Path, State},
    http::{header, StatusCode},
    response::{IntoResponse, Response},
};

use chrono::Utc;
use sky_common::{
    constant::message,
    file::{ensure_dir_exists, UPLOAD_FILE_PATH},
    utils::md5_hash,
};
use tokio::{fs, io::AsyncWriteExt};

pub async fn admin_file_upload(
    _: Claims,
    // State(state): State<AppState>,
    mut multipart: Multipart,
) -> Response<Body> {
    let mut abs_fp = String::new();
    abs_fp.push_str("/");
    while let Some(mut field) = multipart.next_field().await.expect("next field failed") {
        let name = field.name().unwrap_or("unknown").to_string();
        let file_name = field.file_name().unwrap_or("unknown").to_string();
        let content_type = field.content_type().unwrap_or("unknown").to_string();

        let abs_dir = match ensure_dir_exists(chrono::Utc::now().naive_utc()).await {
            Ok(dir) => dir,
            Err(e) => {
                tracing::error!("{}", e);
                return RespVO::<()>::error_without_data(e.to_string()).into_response();
            }
        };
        let struff_fn = format!("{}-{}", Utc::now().timestamp(), md5_hash(&file_name));
        let file_path = format!("{}/{}/{}", UPLOAD_FILE_PATH.as_str(), abs_dir, struff_fn);

        let _ = save_file(file_path.clone(), field).await;

        abs_fp.push_str(&format!("{}/{}", abs_dir, struff_fn));
        // println!("File `{name}` (`{file_name}`: `{content_type}`) saved to `{file_path}`");
    }

    RespVO::<String>::success("File uploaded successfully".to_owned(), Some(abs_fp)).into_response()
}

async fn save_file(fp: String, mut field: multipart::Field<'_>) -> Result<(), Response<Body>> {
    let mut file = match fs::File::create(&fp).await {
        Ok(file) => file,
        Err(err) => {
            tracing::error!("{}", err.to_string());
            return Err(RespVO::<()>::error_without_data(err.to_string()).into_response());
        }
    };

    while let Some(chunk) = field.chunk().await.unwrap_or(None) {
        if let Err(err) = file.write_all(&chunk).await {
            return Err(RespVO::<()>::error_without_data(err.to_string()).into_response());
        }
    }

    Ok(())
}

// get /common/file/2024/11/29/4/1732854058-70a53ae0d0de917765c7c625ac88d234
pub async fn file_download(Path(path): Path<String>) -> Response<Body> {
    let full_path = PathBuf::from(UPLOAD_FILE_PATH.as_str()).join(&path);
    if !full_path.exists() {
        return RespVO::<()>::error_without_data(format!("{path} not exists")).into_response();
    }
    match fs::read(&full_path).await {
        Ok(file_data) => {
            let file_name = full_path.file_name().unwrap_or_default().to_string_lossy();
            Response::builder()
                .status(StatusCode::OK)
                .header(header::CONTENT_TYPE, "application/octet-stream")
                .header(
                    header::CONTENT_DISPOSITION,
                    format!("attachment; filename=\"{}\"", file_name),
                )
                .body(file_data.into())
                .unwrap()
        }
        Err(err) => {
            tracing::error!("{:?}", err);
            RespVO::<()>::error_without_data("Failed to read file".to_owned()).into_response()
        }
    }
}
